﻿using Common.Framework.Core.Foundation;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text.RegularExpressions;

namespace Common.Framework.Core.Swagger
{
    public static class SwaggerExtension
    {
        #region Add Swagger

        /// <summary>
        /// 配置swagger ui
        /// </summary>
        /// <param name="services"></param>
        public static IApiVersionDescriptionProvider AddConfigureSwagger(this IServiceCollection services)
        {
            IApiVersionDescriptionProvider provider = null;
            //控制api版本
            services.AddApiVersioning(o =>
            {
                // 可选，为true时API返回支持的版本信息
                o.ReportApiVersions = true;
                // 不提供版本时，默认为1.0
                o.AssumeDefaultVersionWhenUnspecified = true;
                // 请求中未指定版本时默认为1.0
                o.DefaultApiVersion = new ApiVersion(1, 0);
            }).AddVersionedApiExplorer(option =>
            {
                // 版本名的格式：v+版本号
                option.GroupNameFormat = "'v'V";
                option.AssumeDefaultVersionWhenUnspecified = true;
            });

            provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();

            services.AddSwaggerGen(c =>
            {
                // 多版本控制
                foreach (var item in provider.ApiVersionDescriptions)
                {
                    // 添加文档信息
                    c.SwaggerDoc(item.GroupName, new OpenApiInfo
                    {
                        Title = $"Swagger.NETCore.WebApi v{item.ApiVersion}",
                        Version = item.ApiVersion.ToString(),
                        Description = "ASP.NET CORE WebApi",
                        Contact = new OpenApiContact
                        {
                            Name = "Dongyue.Liu",
                            Email = "liudongyue1987@163.com"
                        }
                    });
                }

                # region 为Swagger添加Jwt
                c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                {
                    Name = "Authorization",
                    BearerFormat = "JWT",
                    Scheme = "Bearer",
                    Type = SecuritySchemeType.ApiKey,
                    In = ParameterLocation.Header,
                    Description = "请在文本框中输入Token. 格式为:Bearer Token",                     
                });
                c.AddSecurityRequirement(new OpenApiSecurityRequirement 
                {
                    {
                         new OpenApiSecurityScheme
                         {
                              Reference = new OpenApiReference
                              {
                                  Type = ReferenceType.SecurityScheme,
                                  Id = "Bearer"
                              }
                         },new string[]{ }
                    }
                });
                #endregion

                #region 注释掉该段后,swagger api可以直接输入版本号调用对应版本的api
                c.OperationFilter<RemoveVersionFromParameter>();
                c.DocumentFilter<ReplaceVersionWithExactValueInPath>();
                #endregion

                //不要xml文件的类库
                List<string> ignoreAssembly = new List<string> { "Common.Framework.Core"};
                Regex ignoreReg = new Regex(".*Module\\w*\\.SDK");

                var assemblyArray = AssemblyFoundation.GetAllProjectAssemblies();
                foreach (var assembly in assemblyArray)
                {
                    string assemblyName = assembly.GetName().Name;

                    //当前项目名需要传进来,否则读取的只是Common.Framework.Core的名称
                    if (!ignoreAssembly.Contains(assemblyName)&& !ignoreReg.IsMatch(assemblyName))
                    {
                        //c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" })
                        var xmlFile = $"{assemblyName}.xml";
                        var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                        c.IncludeXmlComments(xmlPath, includeControllerXmlComments: true);
                    }
                }
            });
            return provider;
        }

        /// <summary>
        /// 注册Swagger中间件
        /// </summary>
        /// <param name="app"></param>
        /// <param name="provider"></param>
        public static void AddUseSwagger(this IApplicationBuilder app, IApiVersionDescriptionProvider provider)
        {
            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                foreach (var item in provider.ApiVersionDescriptions)
                {
                    c.SwaggerEndpoint($"/swagger/{item.GroupName}/swagger.json", item.GroupName.ToUpperInvariant());
                }
                //c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1")
            });
        }

        #endregion
    }

    /// <summary>
    /// 强制swagger api显示对应版本号
    /// </summary>
    public class ReplaceVersionWithExactValueInPath : IDocumentFilter
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="swaggerDoc"></param>
        /// <param name="context"></param>
        public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
        {
            OpenApiPaths paths = new OpenApiPaths();
            foreach (var path in swaggerDoc.Paths)
            {
                paths.Add(path.Key.Replace("{version}", swaggerDoc.Info.Version), path.Value);
            }
            swaggerDoc.Paths = paths;
        }
    }

    /// <summary>
    /// 强制swagger api 调用时不需要输入版本号
    /// </summary>
    public class RemoveVersionFromParameter : IOperationFilter
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="operation"></param>
        /// <param name="context"></param>
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
           if(operation.Parameters.Count>0)
            {
                var versionParameter = operation.Parameters.FirstOrDefault(s => s.Name == "version");
                operation.Parameters.Remove(versionParameter);
            }
        }
    }
}
